use core::{ptr::copy_nonoverlapping, slice};

use alloc::{borrow::ToOwned, vec::Vec};
use driver_utils::conn::xor_sum;
use ty::{NOTIFY, RES, SIMPLE_RES};
use utils::bincodec::{Decode, Encode};

use super::{config::address::{IPC, MY}, error::{AppErr, BaseErr, ErrorCode}};



pub const MIN_LEN: u8 = 9;
pub const MIN_REQ_LEN: u8 = 10;
pub const MIN_RES_LEN: u8 = 11;

pub const HEAD0: u8 = 0xE1;
pub const HEAD1: u8 = 0x1E;
pub const END: u8 = 0xEF;

pub trait Proto<'a> {
    fn serialize(&self, buffer: &'a mut [u8]) -> &'a [u8];
}

impl<'a> Proto<'a> for [u8] {

    fn serialize(&self, buffer: &'a mut [u8]) -> &'a [u8] {
        unsafe {
            let dst = buffer.as_mut_ptr();
            let len = self.len();
            copy_nonoverlapping(self.as_ptr(), dst, len);
            slice::from_raw_parts(dst, len)
        }
    }
}

pub struct Empty {
    ty: u8,
    seq: u8,
    src: u8,
    dst: u8,
}

impl Empty {

    pub fn new(ty: u8, seq: u8, src: u8, dst: u8) -> Self {
        Self { ty, seq, src, dst }
    }

    pub fn new_with_request(request: &RequestRef, ty: u8) -> Self {
        Self { ty, seq: request.seq(), src: request.dst(), dst: request.src() }
    }
}

impl<'a> Proto<'a> for Empty {

    fn serialize(&self, buffer: &'a mut [u8]) -> &'a [u8] {
        let len = MIN_LEN as usize;
        let ptr = buffer.as_mut_ptr();
        unsafe {
            begin(ptr, MIN_LEN, self.ty, self.seq, self.src, self.dst);
            end(ptr, len);
            slice::from_raw_parts(ptr, len)
        }
    }
}

pub struct DataAck {
    basic: Empty,
    ret: u8,
}

impl DataAck {

    pub fn new(request: &RequestRef, ty: u8, ret: u8) -> Self {
        Self {
            basic: Empty {
                ty,
                seq: request.seq(),
                src: request.dst(),
                dst: request.src(),
            },
            ret
        }
    }
}

impl <'a> Proto<'a> for DataAck {

    fn serialize(&self, buffer: &'a mut [u8]) -> &'a [u8] {
        let len = 10;
        let ptr = buffer.as_mut_ptr();
        unsafe {
            begin(ptr, 10, self.basic.ty, self.basic.seq, self.basic.src, self.basic.dst);
            ptr.add(7).write(self.ret);
            end(ptr, len);
            slice::from_raw_parts(ptr, len)
        }
    }
}

pub struct Response <T> {
    pub basic: Empty,
    pub cmd: u8,
    pub ec: u8,
    pub body: T
}

impl <T: Encode> Response<T> {

    pub fn new_simple(request: &RequestRef, body: T) -> Self {
        Self {
            basic: Empty {
                ty: SIMPLE_RES,
                seq: request.seq(),
                src: request.dst(),
                dst: request.src(),
            },
            cmd: request.cmd_or_other(),
            ec: 0,
            body
        }
    } 

    pub fn new(request: &RequestRef, body: T) -> Self {
        Self {
            basic: Empty {
                ty: RES,
                seq: request.seq(),
                src: request.dst(),
                dst: request.src(),
            },
            cmd: request.cmd_or_other(),
            ec: 0,
            body,
        }
    }
}

impl<'a, T: Encode> Proto<'a> for Response<T> {

    fn serialize(&self, buffer: &'a mut [u8]) -> &'a [u8] {
        unsafe {
            let body_len = self.body.to_bytes(&mut buffer[9..]).unwrap();
            let len = body_len + MIN_RES_LEN as usize;
            let ptr = buffer.as_mut_ptr();
            begin(ptr, len as u8, self.basic.ty, self.basic.seq, self.basic.src, self.basic.dst);
            ptr.add(7).write(self.cmd);
            ptr.add(8).write(self.ec);
            end(ptr, len);
            slice::from_raw_parts(ptr, len)
        }
    }
} 

pub struct EmptyResponse {
    pub basic: Empty,
    pub cmd: u8,
    pub ec: u8,
}

impl EmptyResponse {

    pub fn new_simple(request: &RequestRef, ret: &Result<(), AppErr>) -> Self {
        Self {
            basic: Empty {
                ty: SIMPLE_RES,
                seq: request.seq(),
                src: request.dst(),
                dst: request.src(),
            },
            cmd: request.cmd_or_other(),
            ec: ret.ec(),
        }
    } 

    pub fn new_with_ec(request: &RequestRef, ec: u8) -> Self {
        Self {
            basic: Empty {
                ty: RES,
                seq: request.seq(),
                src: request.dst(),
                dst: request.src(),
            },
            cmd: request.cmd_or_other(),
            ec
        }
    }

    pub fn new(request: &RequestRef, ret: &Result<(), AppErr>) -> Self {
        Self {
            basic: Empty {
                ty: RES,
                seq: request.seq(),
                src: request.dst(),
                dst: request.src(),
            },
            cmd: request.cmd_or_other(),
            ec: ret.ec()
        }
    }
}

impl <'a> Proto<'a> for EmptyResponse {

    fn serialize(&self, buffer: &'a mut [u8]) -> &'a [u8] {
        let len = MIN_RES_LEN as usize;
        let ptr = buffer.as_mut_ptr();
        unsafe {
            begin(ptr, MIN_RES_LEN, self.basic.ty, self.basic.seq, self.basic.src, self.basic.dst);
            ptr.add(7).write(self.cmd);
            ptr.add(8).write(self.ec);
            end(ptr, len);
            slice::from_raw_parts(ptr, len)
        }
    }
}

pub struct Cmd<T> {
    basic: Empty,
    cmd: u8,
    body: T,
}

impl <T: Encode> Cmd<T> {

    pub fn new_notify(seq: u8, cmd: u8, body: T) -> Self {
        Self {
            basic: Empty {
                ty: NOTIFY,
                seq,
                src: MY,
                dst: IPC
            },
            cmd,
            body
        }
    }
}

impl <'a, T: Encode> Proto<'a> for Cmd<T> {

    fn serialize(&self, buffer: &'a mut [u8]) -> &'a [u8] {
        unsafe {
            let body_len = self.body.to_bytes(&mut buffer[8..]).unwrap();
            let len = body_len + MIN_REQ_LEN as usize;
            let ptr = buffer.as_mut_ptr();
            begin(ptr, len as u8, self.basic.ty, self.basic.seq, self.basic.src, self.basic.dst);
            ptr.add(7).write(self.cmd);
            end(ptr, len);
            slice::from_raw_parts(ptr, len)
        }
    }
}

unsafe fn begin(ptr: *mut u8, len: u8, ty: u8, seq: u8, src: u8, dst: u8)  {
    ptr.write(HEAD0);
    ptr.add(1).write(HEAD1);
    ptr.add(2).write(len);
    ptr.add(3).write(ty);
    ptr.add(4).write(seq);
    ptr.add(5).write(src);
    ptr.add(6).write(dst);
}

unsafe fn end(ptr: *mut u8, len: usize) {
    let sum = xor_sum(slice::from_raw_parts(ptr.add(3), len - 5));
    ptr.add(len - 2).write(sum);
    ptr.add(len - 1).write(END);
}



pub mod ty {

    pub const SIMPLE_REQ: u8 = 0x00;
    pub const SIMPLE_RES: u8 = 0x01;

    pub const REQ: u8 = 0x02;
    pub const REQ_ACK: u8 = 0x03;

    pub const RES: u8 = 0x04;
    pub const RES_ACK: u8 = 0x05;

    pub const NOTIFY: u8 = 0x06;

    pub const PING: u8 = 0x07;
    pub const PONG: u8 = 0x08;

    pub const CONNECT: u8 = 0x09;
    pub const CONNECT_ACK: u8 = 0x0A;

    pub const POLL: u8 = 0x0B;
    pub const POLL_ACK: u8 = 0x0C;
    pub const LOG: u8 = 0x0D;
}


pub mod poll_ack {
    pub const RUN: u8 = 0;
    pub const FINISH: u8 = 1;
}

pub mod cmd {

    pub const OTA_START: u8 = 0x01;
    pub const OTA_TRANSLATE: u8 = 0x02;
    pub const OTA_COMPLETE: u8 = 0x03;

    #[cfg(feature = "cp_cargo")]
    pub mod cargo {

        pub const STATUS: u8 = 0x00;
        pub const PICK_CTRL: u8 = 0x04;
        pub const COMPR_CTRL: u8 = 0x05;
        pub const CARGO_CTRL: u8 = 0x06;
        pub const SCAN: u8 = 0x07;
        pub const SCAN_LED_CTRL: u8 = 0x08;
        pub const ROW_COL_CTRL: u8 = 0x09;
    }

    #[cfg(feature = "cp_drop")]
    pub mod drop {
        pub const STATUS: u8 = 0x00;
    }
}

/**
 * 0: HEAD0
 * 1: HEAD1
 * 2: len
 * 3: ty
 * 4: seq
 * 5: src
 * 6: dst
 * 7: check_sum
 * 8: END
 */
pub struct RequestRef<'a> {
    pub buf: &'a [u8],
}

impl <'a> RequestRef<'a> {

    pub fn new(buf: &'a[u8]) -> Self {
        Self { buf }
    }

    pub fn ty(&self) -> u8 {
        self.buf[3]
    }

    pub fn seq(&self) -> u8 {
        self.buf[4]
    }

    pub fn src(&self) -> u8 {
        self.buf[5]
    }

    pub fn dst(&self) -> u8 {
        self.buf[6]
    }

    pub fn is_req_length(&self) -> bool {
        self.buf.len() >= (MIN_REQ_LEN as usize)
    }

    pub fn cmd_or_other(&self) -> u8 {
        self.buf[7]
    }

    pub fn body(&self) -> &'a[u8] {
        &self.buf[8..(self.buf.len() - 2)]
    }

    pub fn parse_body<T: Decode<'a>>(&self) -> Result<T, AppErr> {
        T::from_bytes(self.body()).ok_or(AppErr::Base(BaseErr::Parse))
    }

    pub fn to_owned(self) -> Request {
        Request { buf: self.buf.to_owned() }
    }
}

pub struct Request {
    buf: Vec<u8>,
}

impl Request {

    pub fn as_ref<'a>(&'a self) -> RequestRef<'a> {
        RequestRef { buf: &self.buf }
    }
}


